package com.yxx.common.base;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.ui.Model;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import com.yxx.common.global.Constant;
import com.yxx.common.utils.StringUtils;
import com.yxx.module.sys.entity.SysUser;

import tk.mybatis.mapper.util.StringUtil;

public abstract class BaseController implements HandlerExceptionResolver {

	protected static final String ERROR_MSG_KEY = "errorMsg";
	public static final String SUCESS = "success";
	public static final String LIST = "list";
	public static final String FLG = "flg";
	// jsp 返回
	public static final String FAILURE = "failure";
	// app端返回
	public static final String ERROR = "error";
	// 返回json
	public static final String JSON_VIEW = "json/json";

	// *****************************************************************
	protected abstract BaseService<?, Long> getBaseService();

	protected abstract String getPrefix();

	protected final String Add = getPrefix() + "/add";
	protected final String Edit = getPrefix() + "/edit";
	protected final String View = getPrefix() + "/view";
	protected final String List = getPrefix() + "/list";

	/**
	 * ThreadLocal确保高并发下每个请求的request，response都是独立的
	 */
	private static ThreadLocal<ServletRequest> currentRequest = new ThreadLocal<ServletRequest>();
	private static ThreadLocal<ServletResponse> currentResponse = new ThreadLocal<ServletResponse>();
	// private static ThreadLocal<CommonParams> currentParams = new
	// ThreadLocal<CommonParams>();

	/**
	 * 日志对象
	 */
	protected Logger logger = LoggerFactory.getLogger(getClass());

	/**
	 * 跳转到制定模块
	 */
	protected String Move(String fileName){
		return getPrefix() + "/" + fileName;
	}
	
	/**
	 * 跳转到制定模块
	 */
	protected String RedirectMove(String fileName){
//		ModelAndView mAndView = new ModelAndView("redirect:/" + getPrefix() + "/" + fileName);
//        
//        mAndView.addObject(msgKey, msgValue);
//         
//        return mAndView;
		return "redirect:/" + getPrefix() + "/" + fileName;
	}
	
	
	/**
	 * 功能说明：通用跳转到新增页面
	 * 
	 */
	@RequestMapping("add.html")
	protected String add(Model model) {
		model.addAttribute(FLG, "add");
		return Add;
	}

	/**
	 * 功能说明：通用跳转到edit页面
	 * 
	 */
	@RequestMapping(value = "/edit/{id}.html")
	public String edit(Model model, @PathVariable("id") Integer id) {
		model.addAttribute("item", getBaseService().selectByPrimaryKey(id));
		return Edit;
	}

	/**
	 * 功能说明：通用跳转到view页面
	 * 
	 */
	@RequestMapping(value = "/view/{id}.html")
	public String view(Model model, @PathVariable("id") Integer id) {
		model.addAttribute("item", getBaseService().selectByPrimaryKey(id));
		return View;
	}

	/**
	 * 功能说明：通用删除功能
	 * 
	 * @param response
	 *            HttpServletResponse
	 * @param id
	 *            删除的id
	 */
	@RequestMapping(value = "/del/{id}.html")
	@ResponseBody
	public Map<String, String> del(HttpServletResponse response, @PathVariable Integer id) {
		Map<String, String> map = new HashMap<String, String>();
		try {
			if (StringUtil.isNotEmpty(id + "")) {
				getBaseService().deleteByPrimaryKey(id);
				map.put(FAILURE, SUCESS);
				map.put(ERROR_MSG_KEY, "删除成功！");
			} else {
				map.put(FAILURE, ERROR);
				map.put(ERROR_MSG_KEY, "请不要恶意操作！");
			}
		} catch (Exception e) {
			map.put(FAILURE, ERROR);
			map.put(ERROR_MSG_KEY, "删除失败！" + e.getMessage());
			e.printStackTrace();
		}
		return map;
	}

	/**
	 * 线程安全初始化reque，respose对象
	 * 
	 * @param request
	 * @param response
	 */
	@ModelAttribute
	public void initReqAndRep(HttpServletRequest request, HttpServletResponse response) {
		currentRequest.set(request);
		currentResponse.set(response);
	}

	/**
	 * 线程安全
	 */
	public HttpServletRequest request() {
		return (HttpServletRequest) currentRequest.get();
	}

	/**
	 * 线程安全
	 */
	public HttpServletResponse response() {
		return (HttpServletResponse) currentResponse.get();
	}

	/**
	 * 跳转到到登录页面(jsp接口调用)
	 */
	// public ModelAndView toLoginView() {
	// return new ModelAndView("leaguer/loginNew");
	// }

	/**
	 * ① 获取保存在Session中的用户对象
	 */
	protected SysUser getSessionUser(HttpServletRequest request) {
		return (SysUser) request.getSession().getAttribute(Constant.SESSION_USER_KEY);
	}

	/**
	 * ②将用户对象保存到Session中
	 */
	protected void setSessionUser(HttpServletRequest request, SysUser user) {
		request.getSession().setAttribute(Constant.SESSION_USER_KEY, user);
	}

	/**
	 * ③ 获取基于应用程序的url绝对路径
	 */
	public final String getAppbaseUrl(HttpServletRequest request, String url) {
		Assert.hasLength(url, "url不能为空");
		Assert.isTrue(url.startsWith("/"), "必须以/打头");
		return request.getContextPath() + url;
	}

	/**
	 * 添加Model消息
	 * 
	 * @param message
	 */
	protected void addMessage(Model model, String... messages) {
		StringBuilder sb = new StringBuilder();
		for (String message : messages) {
			sb.append(message).append(messages.length > 1 ? "<br/>" : "");
		}
		model.addAttribute("message", sb.toString());
	}

	/**
	 * 添加Flash消息
	 * 
	 * @param message
	 */
	protected void addMessage(RedirectAttributes redirectAttributes, String... messages) {
		StringBuilder sb = new StringBuilder();
		for (String message : messages) {
			sb.append(message).append(messages.length > 1 ? "<br/>" : "");
		}
		redirectAttributes.addFlashAttribute("message", sb.toString());
	}

	// public ModelAndView resolveException(HttpServletRequest request,
	// HttpServletResponse response, Object handler,
	// Exception ex) {
	// ModelAndView mv = new ModelAndView();
	// /* 使用response返回 */
	// response.setStatus(HttpStatus.OK.value()); // 设置状态码
	// response.setContentType(MediaType.APPLICATION_JSON_VALUE); //
	// 设置ContentType
	// response.setCharacterEncoding("UTF-8"); // 避免乱码
	// response.setHeader("Cache-Control", "no-cache, must-revalidate");
	// try {
	// response.getWriter().write("{\"success\":false,\"msg\":\"" +
	// ex.getMessage() + "\"}");
	// } catch (IOException e) {
	// logger.error("与客户端通讯异常:" + e.getMessage(), e);
	// }
	// logger.debug("异常:" + ex.getMessage(), ex);
	// return mv;
	// }

	/**
	 * 异常处理
	 */
	public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
			Exception ex) {
		boolean isajax = isAjax(request, response);
		Throwable deepestException = deepestException(ex);
		return processException(request, response, handler, deepestException, isajax);
	}

	/**
	 * 判断当前请求是否为异步请求.
	 */
	private boolean isAjax(HttpServletRequest request, HttpServletResponse response) {
		return StringUtils.isNoneBlank(request.getHeader("X-Requested-With"));
	}

	/**
	 * 获取最原始的异常出处，即最初抛出异常的地方
	 */
	private Throwable deepestException(Throwable e) {
		Throwable tmp = e;
		int breakPoint = 0;
		while (tmp.getCause() != null) {
			if (tmp.equals(tmp.getCause())) {
				break;
			}
			tmp = tmp.getCause();
			breakPoint++;
			if (breakPoint > 1000) {
				break;
			}
		}
		return tmp;
	}

	/**
	 * 处理异常.
	 *
	 * @param request
	 * @param response
	 * @param handler
	 * @param deepestException
	 * @param isajax
	 * @return
	 */
	private ModelAndView processException(HttpServletRequest request, HttpServletResponse response, Object handler,
			Throwable ex, boolean isajax) {
		// 步骤一、异常信息记录到日志文件中.
		logger.error("全局处理异常捕获:", ex);
		// 步骤二、异常信息记录截取前50字符写入数据库中.
		// TODO
		// 步骤三、分普通请求和ajax请求分别处理.
		if (isajax) {
			return processAjax(request, response, handler, ex);
		} else {
			return processNotAjax(request, response, handler, ex);
		}
	}

	/**
	 * ajax异常处理并返回.
	 *
	 * @param request
	 * @param response
	 * @param handler
	 * @param deepestException
	 * @return
	 */
	private ModelAndView processAjax(HttpServletRequest request, HttpServletResponse response, Object handler,
			Throwable deepestException) {
		ModelAndView empty = new ModelAndView();

		/* 使用response返回 */
		response.setStatus(HttpStatus.OK.value()); // 设置状态码
		response.setContentType(MediaType.APPLICATION_JSON_VALUE); //
		// 设置ContentType
		response.setCharacterEncoding("UTF-8"); // 避免乱码
		response.setHeader("Cache-Control", "no-cache, must-revalidate");
		try {
			response.getWriter().write("{\"success\":false,\"msg\":\"" + deepestException.getMessage() + "\"}");
		} catch (IOException e) {
			logger.error("与客户端通讯异常:" + e.getMessage(), e);
		}
		empty.clear();
		return empty;
	}

	/**
	 * 普通页面异常处理并返回.
	 *
	 * @param request
	 * @param response
	 * @param handler
	 * @param deepestException
	 * @return
	 */
	private ModelAndView processNotAjax(HttpServletRequest request, HttpServletResponse response, Object handler,
			Throwable ex) {
		String exceptionMessage = getThrowableMessage(ex);
		Map<String, Object> model = new HashMap<String, Object>();
		model.put("exceptionMessage", ex.getMessage());
		model.put("ex", exceptionMessage);
		model.put("exCode", ex.getCause());
		return new ModelAndView("error/error", model);
	}

	/**
	 * 返回错误信息字符串
	 *
	 * @param ex
	 *            Exception
	 * @return 错误信息字符串
	 */
	public String getThrowableMessage(Throwable ex) {
		StringWriter sw = new StringWriter();
		PrintWriter pw = new PrintWriter(sw);
		ex.printStackTrace(pw);
		return sw.toString();
	}

}
